﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using CInChat.Communication;
using System.ServiceModel.Description;
using CInChat.Security;

namespace CInChat.Communication
{
    public class ChatController : IChat
    {
        public string User { get; private set; }
        public IList<string> AllUsers { get; set; }

        private IChatChannel channel;
        private IOnlineStatus status;
        private InstanceContext host;
        private DuplexChannelFactory<IChatChannel> factory;

        public ChatController(string userName) {
            this.User = userName;
            this.AllUsers = new List<string>();
        }

        public void StartUp() {
            if (host != null) {
                return;
            }

            host = new InstanceContext(this);
            factory = new DuplexChannelFactory<IChatChannel>(host, "CInChatEndpoint");
            channel = factory.CreateChannel();
            status = channel.GetProperty<IOnlineStatus>();
            status.Online += new EventHandler(status_Online);
            status.Offline += new EventHandler(status_Offline);
            channel.WarmUp();
        }

        public void Close() {
            channel.Close();
        }

        public void SendMessage(string user, string message) {
            this.channel.ReceiveMessage(user, message);
        }

        public void Whisper(string from, string to, string message) {
            string encryptedMessage = Crypto.EncryptStringAES(message, from);
            this.channel.ReceiveWhisper(from, to, encryptedMessage);
        }

        public void NotifyUserLeft(string user) {
            this.channel.LeaveChat(user);
        }

        #region Status events
        private void status_Online(object sender, EventArgs e) {
            this.channel.JoinChat(User);
            OnStarted(e);
        }

        private void status_Offline(object sender, EventArgs e) {
            channel.LeaveChat(User);
        }
        #endregion

        #region IChat
        public void WarmUp() {
            this.channel.UpdateUserList(User);
        }

        public void JoinChat(string user) {
            if (!AllUsers.Contains(user)) {
                AllUsers.Add(user);
                OnUserListUpdated(new UserListEventArgs { AllUsers = this.AllUsers });
            }
            this.channel.UpdateUserList(user);
            OnUserJoined(new UserEventArgs { User = user });
        }

        public void LeaveChat(string user) {
            if (user != this.User) {
                this.AllUsers.Remove(user);
                OnUserLeft(new UserEventArgs { User = user });
                OnUserListUpdated(new UserListEventArgs { AllUsers = AllUsers });
            }
        }

        public void ReceiveMessage(string user, string message) {
            OnMessageReceived(new MessageEventArgs { From = user, Message = message });
        }

        public void ReceiveWhisper(string from, string to, string message) {
            if (User.Equals(from) || User.Equals(to)) {
                string decryptedMessage = Crypto.DecryptStringAES(message, from);
                OnMessageReceived(new MessageEventArgs { From = from, Message = decryptedMessage });
            }
        }

        public void UpdateUserList(string user) {
            if (!AllUsers.Contains(user)) {
                AllUsers.Add(user);
            }
            OnUserListUpdated(new UserListEventArgs { AllUsers = this.AllUsers });
        }
        #endregion //IChat

        #region Events
        public event UserEventHandler UserJoined;
        public event UserEventHandler UserLeft;
        public event MessageEventHandler MessageReceived;
        public event MessageEventHandler Whipered;
        public event UserListEventHandler ListUpdated;
        public event EventHandler Started;
        #endregion //Events

        #region OnEvents
        public virtual void OnUserJoined(UserEventArgs e) {
            if (UserJoined != null)
                UserJoined(this, e);
        }

        public virtual void OnUserLeft(UserEventArgs e) {
            if (UserLeft != null)
                UserLeft(this, e);
        }

        public virtual void OnMessageReceived(MessageEventArgs e) {
            if (MessageReceived != null)
                MessageReceived(this, e);
        }

        public virtual void OnWhipered(MessageEventArgs e) {
            if (Whipered != null)
                Whipered(this, e);
        }

        public virtual void OnUserListUpdated(UserListEventArgs e) {
            if (ListUpdated != null)
                ListUpdated(this, e);
        }

        public virtual void OnStarted(EventArgs e) {
            if (Started != null)
                Started(this, e);
        }
        #endregion //OnEvents
    }
}
